package com.librariy.view;
import java.util.ArrayList;
import java.util.List;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.HeaderViewListAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Scroller;
import com.bcia.assetmange.R;
import com.librariy.util.Log;
/**
 * 用法同ListView, 异步分段加载数据的ListView。
 * 
 * @author luchyu
 * */
public class AsyncListView extends ListView {
    private static final String TAG = AsyncListView.class.getSimpleName();

    private enum Status {
        None, Loading, Pressed
    }

    private Scroller mScroller = new Scroller(getContext(), new DecelerateInterpolator(1f));
    private Drawable mEmptyImage;
    private RingPicture mRingPicture = new RingPicture();
    private int mPageSize = 30;
    private Status mStatus = Status.None;
    /** [0-加载完毕；1-加载未完毕；2-加载失败] */
    private int mLoadType = 1;

    public AsyncListView(Context context) {
        super(context);
        this.initialise();
    }

    public AsyncListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.initialise();
    }

    public void setEmptyImage(int imgResId) {
        this.mEmptyImage = getResources().getDrawable(imgResId);
        this.mEmptyImage.setBounds(0, 0, mEmptyImage.getIntrinsicWidth(), mEmptyImage.getIntrinsicWidth());
    }

    public AsyncListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.initialise();
    }

    @Override
    public void setAdapter(ListAdapter adapter) {
        if (adapter instanceof AsyncListAdapter<?>) {
            ((AsyncListAdapter<?>) adapter).setListView(this);
        }
        super.setAdapter(adapter);
    }

    private AsyncListAdapter<?> getAsynAdapter() {
        ListAdapter retListAdapter = super.getAdapter();
        if (retListAdapter instanceof HeaderViewListAdapter) {
            retListAdapter = ((HeaderViewListAdapter) retListAdapter).getWrappedAdapter();
        }
        if (retListAdapter instanceof AsyncListAdapter<?>) {
            return (AsyncListAdapter<?>) retListAdapter;
        }
        return null;
    }

    public boolean isItemCheckable(int position) {
        if (super.getAdapter() instanceof AsyncListAdapter<?>) {
            return ((AsyncListAdapter<?>) super.getAdapter()).isCheckable(position);
        }
        return false;
    }
    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
    private void initialise() {
        this.setEmptyImage(R.drawable.bg_listview_empty);
        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.GINGERBREAD){
            super.setOverScrollMode(View.OVER_SCROLL_NEVER);
        }
    }
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (super.getChildCount() < 1) {
            return;
        }
        if (mStatus != Status.None)
            return;
        if ((super.getLastVisiblePosition() != super.getCount() - 1)) {
            return;
        }
        Log.d(TAG, String.format("onScrollChanged(l=%d, t=%d, oldl=%d, l=%d,mLoadType=%d)", l, t, oldl, oldt, mLoadType));
        if (mLoadType != 1) return;
        AsyncListAdapter<?> mAdapter=getAsynAdapter();
        if(mAdapter==null) return;
        loadMore(mAdapter.getCount());
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (mStatus == Status.Loading)
            return true;
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mStatus = Status.Pressed;
                smoothMove(ev.getY(), 0, 0);
                break;
            case MotionEvent.ACTION_MOVE:
                float offsetY = ev.getY() - mScroller.getStartY();
                if(!canMove()||offsetY<-5){//允许-5的误差
                    smoothMove(ev.getY(), 0, 0);
                }else{
                    int mDistanceY=mScroller.getCurrY();
                    if(mDistanceY>RingPicture.MinHeight*3){
                        mDistanceY=mDistanceY+(offsetY>0?2:-2);
                    }else{
                        mDistanceY+=offsetY;
                    }
                    smoothMove(ev.getY(), mDistanceY, 0);
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                mStatus = Status.None;
                if (mScroller.getCurrY() >= RingPicture.MinHeight) {
                    smoothMove(RingPicture.MinHeight, 500);
                    loadMore(0);
                }else if (mScroller.getCurrY() >0){
                    smoothMove(0, 500);
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
    private boolean canMove() {
        if(getChildCount()<1)  return true;
        if(super.getFirstVisiblePosition()!=0) return false;
        if(getChildAt(0).getTop()<0)  return false;
        return true;
    }
    public void smoothMove(float endY,int duration) {
        this.smoothMove(mScroller.getCurrY(), endY, duration);
    }
    public void smoothMove(float startY, float endY, int duration) {
        mScroller.startScroll(0, (int)startY, 0, (int)(endY - startY), 500);
        if (startY == endY || duration<=0) {
            mScroller.abortAnimation();
        }
        super.postInvalidate();
    }
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (super.getChildCount() < 1 && mEmptyImage != null) {
            int width = mEmptyImage.getIntrinsicWidth();
            int height = mEmptyImage.getIntrinsicHeight();
            if (width > getMeasuredWidth()) {
                width = getMeasuredWidth();
            }
            if (height > getMeasuredHeight()) {
                height = getMeasuredHeight();
            }
            int left = getScrollX() + (getMeasuredWidth() - width) / 2;
            int top = getScrollY() + (getMeasuredHeight() - height) / 2;
            mEmptyImage.setBounds(left, top, left + width, top + height);
            mEmptyImage.draw(canvas);
        }
        canvas.save();
        if (mStatus == Status.Pressed) {
            int offsetY = mScroller.getCurrY();
            // 手动控制
            if (offsetY < 0)
                return;
            int mAngle = offsetY;
            int sweepAngle = mAngle < 300 ? mAngle : 300;
            int startAngle = (mAngle < 300) ? (mAngle / 2) : (mAngle - 150) % 360;
            //
            float mScale = (Math.abs(offsetY) * 1.0f / mRingPicture.height());
            if (mScale < 1) {
                canvas.translate(getWidth() / 2 - mRingPicture.width() / 2, 0);
                canvas.scale(mScale, mScale, mRingPicture.centerX(), mRingPicture.centerY());
            } else {
                canvas.translate(getWidth() / 2 - mRingPicture.width() / 2, offsetY - mRingPicture.height());
            }
            mRingPicture.drawRingPicture(canvas,startAngle, sweepAngle, true);
        } else if (mScroller.computeScrollOffset()) {
            // 自动弹回
            // 1500毫秒旋转720度
            int sweepAngle = (int) ((SystemClock.elapsedRealtime() % 1500) * (720f / 1500f)); // sweepAngle在1500毫秒分布在[0~2000]
            int startAngle = sweepAngle / 2;
            sweepAngle = (sweepAngle / 10) * 10;
            sweepAngle = (sweepAngle < 360) ? sweepAngle : (sweepAngle - 720);
            //
            int offsetY = mScroller.getCurrY();
            float mScale = (Math.abs(offsetY) * 1.0f / mRingPicture.height());
            if (mScale < 1) {
                canvas.translate(getWidth() / 2 - mRingPicture.width() / 2, 0);
                canvas.scale(mScale, mScale, mRingPicture.centerX(), mRingPicture.centerY());
            } else {
                canvas.translate(getWidth() / 2 - mRingPicture.width() / 2, offsetY - mRingPicture.height());
            }
            mRingPicture.drawRingPicture(canvas,startAngle, sweepAngle, false);
            super.postInvalidate();
        } else if (mStatus == Status.Loading) {
            // 1500毫秒旋转720度
            int sweepAngle = (int) ((SystemClock.elapsedRealtime() % 1500) * (720f / 1500f)); // sweepAngle在1500毫秒分布在[0~2000]
            int startAngle = sweepAngle / 2;
            sweepAngle = (sweepAngle / 2) * 2;
            sweepAngle = (sweepAngle < 360) ? sweepAngle : (sweepAngle - 720);
            canvas.translate(getWidth() / 2 - mRingPicture.width() / 2, mScroller.getFinalY()-RingPicture.MinHeight);
            mRingPicture.drawRingPicture(canvas,startAngle, sweepAngle, false);
            super.postInvalidateDelayed(10);
        }
        canvas.restore();
    }
    /**
     * mAnimType：[0-无动画；1-顶部动画, 2-底部动画]
     */
    public synchronized void loadMore(final int position) {
        if (mStatus == Status.Loading) {
            return;
        }
        Log.d(TAG, "loadMore，postion=" + position);
        if (!(getAsynAdapter() instanceof AsyncListAdapter)) {
            dispatchLoadSuccess(position,2);
            return;
        }
        this.mLoadType = 1;
        mStatus = Status.Loading;
        if(position>0){
            int posY=RingPicture.MinHeight;
            if(getChildCount()>0){
                posY=getChildAt(getChildCount()-1).getBottom()+RingPicture.MinHeight;
            }
            if(posY>getMeasuredHeight()){
                posY=getMeasuredHeight();
            }
            smoothMove(0,posY, 0);
        }else if(mScroller.isFinished()){
           smoothMove(0, RingPicture.MinHeight, 500);
        }
        //加入加载特效，显示1秒的加载动画
        this.postDelayed(new Runnable() {
            @Override
            public void run() {
                getAsynAdapter().loadData(position, (position + mPageSize) / mPageSize, mPageSize);
            }
        },1000);
    }

    /**
     * mLoadingType：[0-加载完毕；1-加载未完毕；2-加载失败]
     */
    private void dispatchLoadSuccess(int position, int mLoadType) {
        this.mLoadType = mLoadType;
        this.mStatus = Status.None;
        smoothMove(0, position>0?0:1500);
        Log.d(TAG, "dispatchLoadSuccess，postion=" + position + "; mLoadingType=" + mLoadType);
    }

    public static abstract class AsyncListAdapter<T> extends BaseAdapter {
        protected static final String TAG = AsyncListAdapter.class.getSimpleName();
        protected AsyncListView listView;
        protected ArrayList<T> mData = new ArrayList<T>();

        public AsyncListAdapter() {
        }

        protected void setListView(AsyncListView listView) {
            this.listView = listView;
        }

        protected Context getContext() {
            return listView.getContext();
        }

        @Override
        public int getCount() {
            return mData.size();
        }

        protected void setData(int position, List<T> mList) {
            if (mList == null)
                mList = new ArrayList<T>(0);
            // eg: children.size()=9, startIndex=10;
            while (mData.size() < position) {
                mData.add(null);
            }
            // eg: children.size()=11, startIndex=10;
            while (position < mData.size() && !mData.isEmpty()) {
                mData.remove(mData.size() - 1);
            }
            for (T item : mList) {
                mData.add(item);
            }
            if (position == 0) {
                super.notifyDataSetInvalidated();
            } else {
                super.notifyDataSetChanged();
            }
        }

        @Override
        public T getItem(int position) {
            if (position < 0 || position > mData.size() - 1) {
                Log.d(TAG, "Info: getItem(" + position + "), mData.size()=" + mData.size() + ",  Wrong item position[" + position + "]");
                return null;
            }
            return mData.get(position);
        }

        @Override
        public boolean hasStableIds() {
            return true;
        }

        public boolean isCheckable(int position) {
            return (listView.getChoiceMode() != AbsListView.CHOICE_MODE_NONE);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        /**
         * mLoadingType：[0-加载完毕；1-加载未完毕；2-加载失败]
         */
        protected void dispatchLoadSuccess(int position, List<T> result, int mLoadedType) {
            this.setData(position, result);
            listView.dispatchLoadSuccess(position, mLoadedType);
        }

        protected abstract void loadData(final int position, final int pageNum, final int pageSize);
    }

    /**
     * 圆环图像对象
     */
    private static class RingPicture extends RectF {
        // 全局参数
        public final static float DP = Resources.getSystem().getDisplayMetrics().widthPixels * 1.0f / 360;
        public final static float RADIUS = 30 * DP;
        public final static int MinHeight = (int) (2 * RADIUS);
        private RectF mRingRect;
        private Paint mShapePaint;
        private Path path = new Path();
        public Bitmap mBgBitmap = null;
        private String mLastPare = "";

        public RingPicture() {
            super(0, 0, 2 * RADIUS, 2 * RADIUS);
            this.mRingRect = new RectF(left + 18 * DP, top + 18 * DP, right - 18 * DP, bottom - 18 * DP);
            this.mBgBitmap = Bitmap.createBitmap((int) width(), (int) height(), Bitmap.Config.ARGB_8888);
            // 1.生成背景
            Paint mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mBgPaint.setColor(0xFFFFFFFF);
            mBgPaint.setStyle(Paint.Style.FILL_AND_STROKE);
            mBgPaint.setShadowLayer(3 * DP, 0, 0, 0xFFCCCCCC);
            Canvas c = new Canvas(mBgBitmap);
            c.drawCircle(centerX(), centerY(), RADIUS - 12 * DP, mBgPaint);
            // 2.圆环动画画笔
            mShapePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mShapePaint.setAntiAlias(true);
            mShapePaint.setColor(0xFF333333);
            mShapePaint.setStyle(Paint.Style.STROKE);
            mShapePaint.setStrokeWidth((float) (2.5 * DP));
        }

        public boolean drawRingPicture(Canvas c,int startAngle, int sweepAngle, boolean showArrow) {
            if (TextUtils.equals(mLastPare, "" + startAngle + sweepAngle + showArrow))
                return false;
            this.mLastPare = "" + startAngle + sweepAngle + showArrow;
            c.drawBitmap(mBgBitmap, 0, 0, null);
            // 2.画圆环
            c.drawArc(mRingRect, startAngle, sweepAngle, false, mShapePaint);
            if (!showArrow)
                return true;
            // 3.画箭头
            int endAngle = startAngle + sweepAngle;
            float radius = mRingRect.height() / 2;
            double x1 = mRingRect.centerX() + radius * Math.cos(endAngle * Math.PI / 180);
            double y1 = mRingRect.centerY() + radius * Math.sin((endAngle * Math.PI / 180));
            // 点(x1,y1)上的切线角度（3点钟方向为0°角）
            int tangentAngle = endAngle - 90;
            // 方向校正，保证箭头看起来更合理
            tangentAngle -= 15;
            double x2 = x1 + (6 * DP) * Math.cos((tangentAngle - 30) * Math.PI / 180);
            double y2 = y1 + (6 * DP) * Math.sin((tangentAngle - 30) * Math.PI / 180);
            double x3 = x1 + (6 * DP) * Math.cos((tangentAngle + 30) * Math.PI / 180);
            double y3 = y1 + (6 * DP) * Math.sin((tangentAngle + 30) * Math.PI / 180);
            path.rewind();
            path.setLastPoint((float) x1, (float) y1);
            path.lineTo((float) x2, (float) y2);
            path.lineTo((float) x3, (float) y3);
            path.lineTo((float) x1, (float) y1);
            path.close();
            c.drawPath(path, mShapePaint);
            return true;
        }
    }

}